package com.choupin.choupin.service.impl;

import com.choupin.choupin.entity.*;
import com.choupin.choupin.mapper.LotterypoolMapper;
import com.choupin.choupin.mapper.MemberMapper;
import com.choupin.choupin.mapper.OrderMapper;
import com.choupin.choupin.mapper.UserMapper;
import com.choupin.choupin.service.ILotteryPool;
import com.choupin.choupin.service.exception.SelectExcepion;
import com.choupin.choupin.service.exception.ParameterIsEmptyException;
import com.choupin.choupin.utils.StrUtil;
import com.choupin.choupin.utils.redis.RedisHashUtil;
import com.choupin.choupin.utils.redis.RedisMessageQueue;
import com.choupin.choupin.utils.redis.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.jws.soap.SOAPBinding;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@SuppressWarnings("ALL")
@Service
public class LotteryPoolImpl implements ILotteryPool {


    @Autowired
    private LotterypoolMapper lotterypoolMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private MemberMapper memberMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private RedisUtils redisUtils;



    @Override
    public PoolListAndPool  jumpLotteryPool(String topName,String phone) {
//        参数校验
         if (topName==null|| StrUtil.isBlank(topName)){
             throw new ParameterIsEmptyException("网络出错了请从新刷新一下！");
         }
//         查询对应抽奖池表信息
        Lottery lottery = lotterypoolMapper.selectOneTopName(topName);
          if (lottery==null){
              throw new SelectExcepion("抽奖池不存在！");
          }
        System.out.println("查看到的抽奖信息"+lottery);
//          通过抽奖池id查询转盘表信息
        List<Turntable> list = lotterypoolMapper.selectOneTurntable(lottery.getId());
        System.out.println("查看到的转盘信息"+list);
        if (list.size()<=0||list==null){
           throw new SelectExcepion("转盘信息不存在！");
        }
//        User user=userMapper.selectPhone(phone);
//        if (user==null){
//            throw  new SelectExcepion("用户不存在！");
//        }
        return  new PoolListAndPool(lottery.getId(),lottery.getName(),lottery.getInstructions(),lottery.getPrice(),list) ;
    }


    //     将抽奖用户存入消息队列
    @Override
    public void  addMessage(String phone, String queueMessage) {
//        参数校验
        if ((phone==null||phone=="")||(queueMessage==null||queueMessage=="")){
            throw new ParameterIsEmptyException("参数为空或者空字符串！");
        }
        System.out.println("存入队列的手机号为："+phone+"队列名称是："+queueMessage);
//        将用户存入消息队列
        RedisMessageQueue.addMessageQueue(phone,queueMessage);
        System.out.println("-----------添加队列成功-----------");
    }




//    中奖逻辑实现
    @Override
    public synchronized void winPrize(String queueMessage,String topName) {
//        参数校验
        if (queueMessage==null||topName==null){
            throw new ParameterIsEmptyException("queueMessage参数为空！");
        }
        System.out.println("-----------开始中奖逻辑-----------");
//  1.从redis消息队列中获取用户手机号
       Set<String> messsages=RedisMessageQueue.getMessageQueue(queueMessage);
        System.out.println("-----------获取队列大小-----------");
        System.out.println("队列大小："+messsages.size()+"队列中参数是："+messsages);
//  2.zset集合为空直接返回(它为空说明上一轮他已经被计算过了)
          if (messsages.size()==0||messsages.size()<0){
              return;
          }
        Iterator<String> iterator=messsages.iterator();
        // 获取当前时间
        LocalDateTime localDateTime=LocalDateTime.now();
        DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String prizeTime=localDateTime.format(dateTimeFormatter);
//  3.初始化Hash表
        for (String message:messsages){
//            如果取出的是空字符串则直接进入下一轮循环
            if (message==""){
                continue;
            }
            RedisHashUtil.hset("lottery:prize",message,prizeTime);
        }
        System.out.println("-----------存储中奖时间的hash表初始化完成-----------");
//  4.如果转盘表还有礼品数量则继续往下执行
//                   计数开关
                   Integer count=0;
//               通过抽奖池名称查询抽奖池表信息
                Lottery poolList=lotterypoolMapper.selectOneTopName(topName);
                if (poolList==null){
                    throw new SelectExcepion("查询抽奖池目录表失败！");
                }
//               通过抽奖池表id查询转盘表转盘数为2和4的数量
             List<Turntable> turntables=lotterypoolMapper.selectOneTurntable(poolList.getId());
                for (Turntable t:turntables){
                    if ((t.getTurntableId()==2&&t.getAmount()==0)||(t.getTurntableId()==4&&t.getAmount()==0)){
                        System.out.println("-----------2或4号转盘奖品数量-----------");
                        System.out.println("2或4号转盘奖品数量："+t.getAmount());
                        count++;
                    }
                }
//                商品已经抽完
                      if (count==2){
//                      清除zset缓存中的数据
                          while (iterator.hasNext()){
                              String message=iterator.next();
                              RedisMessageQueue.deleteMessageQueue(queueMessage,message);
                              iterator.remove();
                          }
                          System.out.println("-----------奖品已经抽完----------");
                          return;
                      }
//  5.将今天内已经中奖的和近三天内已经中过两次奖排除
//        遍历数据
        while (iterator.hasNext()){
            String message=iterator.next();
//            判断队列内容是否为空字符串
            if (message==""){
                RedisMessageQueue.deleteMessageQueue(queueMessage,message);
//              将该元素从Set集合中剔除
                iterator.remove();
//               判断此时队列集合是否就被清空了（当只有一个用户进来而且是队列中存储的是空值）
                //            当只有一个中过奖的用户进来，还被剔除了则直接返回
                if (messsages.size()<=0||messsages==null){
                    System.out.println("清空一个为空的错误队列后，队列已经没有其他人清空直接返回！");
                    return;
                }
//                    移除该队列之后要跳出这轮循环，进入下一轮
                    continue;
            }

            //        从数据库中获取中奖数据
            Prize prize=lotterypoolMapper.selectPrizeOnePhone(message);
            System.out.println("-----------查看用户以前的中奖数据-----------");
            System.out.println(prize);
//            prize不为null则执行
            if (prize!=null){
                //            计算时间差
                Duration duration=Duration.between(prize.getPrizeTime(),localDateTime);
                System.out.println("-----------计算距离上一次中奖的时间差-----------");
                System.out.println(duration.toHours());
                if(duration.toHours()<24||duration.toHours()<72){
                    RedisMessageQueue.deleteMessageQueue(queueMessage,message);
//              将该元素从Set集合中剔除
                    iterator.remove();
//                    移除该队列之后要跳出这轮循环，进入下一轮
//                    continue;
                }
            }
//            当只有一个中过奖的用户进来，还被剔除了则直接返回
            if (messsages.size()<=0||messsages==null){
                System.out.println("此时已经没有一个用户满足条件，队列别清空直接返回！");
                return;
            }

//            通过手机号查询用户表信息
            User user=userMapper.selectPhone(message);
            System.out.println("-----------查出用户私人数据-----------从队列中取出的手机号到底是什么！"+message);
            System.out.println(user.getPhone()+":"+user);
            //             参数检验
            if (user==null){
                throw  new SelectExcepion("获取到用户会员表数据错误！");
            }
//            判断vip用户多少人
            if (user.getIsVip()==1){
                count++;
            }
        }
        //  将普通用户剔除
            if (count>0){
                while (iterator.hasNext()){
                    String message=iterator.next();
                        //   通过手机号查询用户表信息
                        User user=userMapper.selectPhone(message);
                        //   参数检验
                        if (user==null){
                            throw  new SelectExcepion("获取到用户表数据错误！");
                        }
//                    剔除普通用户
                        if (user.getIsVip()==0){
                            RedisMessageQueue.deleteMessageQueue(queueMessage,message);
                            iterator.remove();
                        }
                }

//                如果此时只有一个不符合条件的用户进来，被清空了则就直接返回
                if (messsages.size()==0){
                    System.out.println("---------- 如果此时只有一个不符合条件的用户进来被清空了结束------------");
                    return;
                }
                System.out.println("-----------不符合条件的用户已被清楚-----------");
            }

//  6.创建一个数组用于存放中奖概率，中奖概率排在前面的肯定是vip用户，即使是相等的概率也不用区分是谁的，
//       既然是相等概率，那么随机分配回他们的排序情况也平等的，相等时谁在前谁在后都没关系。
        Integer[] arr=new Integer[messsages.size()];
//        从数据库中查出用户对应的概率存入数组
        Integer i=0;
        for (String message:messsages){
            User user=userMapper.selectPhone(message);
           arr[i]=user.getWeight();
           i++;
       }
//  7.排序
//       根据数量选择算法
         if (messsages.size()<1000){
//             //        小于1000人使用冒泡排序
             System.out.println("-----------冒泡算法排序-----------");
             bubbleSort(arr);
             System.out.println("-----------排序完成-----------");
         }else {
//             //        大于1000人使用快速排序
             System.out.println("-----------快速算法排序-----------");
             quickSort(arr,0,messsages.size());
             System.out.println("-----------排序完成-----------");
         }
        Integer groupWeight=0;
// 8.计算他们群体可能中奖概率
        for (int j=0;j<arr.length;j++){
             groupWeight+=arr[j];
        }
        System.out.println("群体概率为："+groupWeight);
        groupWeight=groupWeight/arr.length;
//        生成一个0到100的随机数
        Random random=new Random();
        Integer randomNumber=random.nextInt(101);
//       判断几率，大于75则中奖，因为这个公式的最大概率是50
        Integer prizeWinning=(randomNumber+groupWeight)/2;
        if (prizeWinning>9) {
            System.out.println("-----------当前全体有几率中奖-----------");
            Integer prandom=random.nextInt(2);
//           满足条件的用户只有一个则中奖就是他，否则进行第二步比较
            if (arr.length > 0 && arr.length < 2) {
//                //  11.取最大概率者中奖
                String[] str=new String[1];
//              取出最大概率用户
                for (String m:messsages){
                   User user=userMapper.selectPhone(m);
                    if (arr[0]==user.getWeight()){
                        str[0]=m;
                    }
                }
                System.out.println("---------只有一个中奖者符合得条件出中奖者-----------");
                System.out.println("中奖者："+str[0]);
//                生成奖品，数量减少，删除队列
                addPrize(turntables,str[0],prandom,iterator,localDateTime,queueMessage);
            }else {
                System.out.println("---------有几个中奖者符合得条件需要进行计算-----------");
//  9.几个用户抽奖概率相等
                if (arr[0] == arr[1]) {
                    i = 2;
                    for (Integer j = 2; j < arr.length; j++) {
                        if (arr[0] == arr[j]) {
//                      统计最大概率相等的人数
                            i++;
                        }
                    }
//             数组下标
                    Integer strIndex = i;
                    String[] str = new String[strIndex];
                    i = 0;
//             取出最大概率相等的用户
                    for (String m : messsages) {
                        User user = userMapper.selectPhone(m);
                        if (arr[0] == user.getWeight()) {
                            str[i] = m;
                            i++;
                        }
                    }
//             以数组为范围随机选择
                    Integer randomStr = random.nextInt(strIndex);
//              确定中奖者
                    String phonePrize = str[randomStr];
                    System.out.println("得出中奖者：" + phonePrize);
//
//             通过抽奖池名称查寻抽奖池表信息
                    Lottery lottery = lotterypoolMapper.selectOneTopName(topName);
//              通过抽奖池id查询转盘表信息
                    List<Turntable> pools = lotterypoolMapper.selectOneTurntable(lottery.getId());
//             遍历list集合
                    addPrize(turntables, phonePrize, prandom, iterator, localDateTime, queueMessage);
                } else {
                    System.out.println("-----------有多个用户符合条件，但是最大概率不想等-----------");
//  11.取最大概率者中奖
                    String[] str = new String[1];
//             取出最大概率用户
                    for (String m : messsages) {
                        User user = userMapper.selectPhone(m);
                        if (arr[0] == user.getWeight()) {
                            str[0] = m;
                        }
                    }
                    System.out.println("得出中奖者：" + str[0]);
                    addPrize(turntables,str[0],prandom,iterator,localDateTime,queueMessage);
                }
            }
        }else {
//           清空zset缓存
            while (iterator.hasNext()){
                String message=iterator.next();
                RedisMessageQueue.deleteMessageQueue(queueMessage,message);
                iterator.remove();
            }
            System.out.println("-----------很遗憾，群体没中奖！-----------");
        }
    }

    //    冒泡排序
    public void bubbleSort(Integer[] arr){
        for (Integer i=0;i<arr.length-1;i++){ // 外层循环控制排序趟数
            for (Integer j=0;j<arr.length-1-i;j++){ // 内层循环控制每趟比较的次数
                if (arr[j]<arr[j+1]){             //降序，小的往后排
                    Integer temp=arr[j+1];
                    arr[j+1]=arr[j];
                    arr[j]=temp;
                }
            }
        }
    }

    //     快速排序
    public void quickSort(Integer[] arr,Integer low,Integer high){
        if (low<high){
            Integer pivot=partition(arr,low,high);
            quickSort(arr,low,pivot-1);
            quickSort(arr,pivot+1,high);
        }
    }

    //    获取基准元素
    public Integer partition(Integer[] arr,Integer low ,Integer high){
//      确定基准元素
        Integer pivot=arr[low];
        while (low<high){
//            对比右边元素，小于基准元素右指针减减
            while (low<high&&arr[high]<pivot){
                high--;
            }
//            否则，将右边元素掉回左边
            arr[low]=arr[high];
//            对比左边元素，大于基准元素左指针加加
            while (low<high&&arr[low]>pivot){
                low++;
            }
//            否则，将左边元素掉回右边
            arr[high]=arr[low];
        }
//        比较晚一轮之后，基准元素来到了中间
        arr[low]=pivot;
        return low;
    }

//      写入中奖表
            public void addPrize(List<Turntable> turntables ,String phone,Integer prandom,Iterator<String> iterator,LocalDateTime localDateTime,String queueMessage){
        for(Turntable p:turntables){
        if((p.getTurntableId()==2&&prandom==0)||(p.getTurntableId()==4&&prandom==1)){
//                      将该用户数据写入奖品表
        Prize prize=new Prize();
        prize.setPhone(phone);
        prize.setTurnId(p.getId());
        prize.setTurntableId(p.getTurntableId());
        prize.setName(p.getPrizeName());
        prize.setImage(p.getImage());
        prize.setPrizeTime(localDateTime);
        prize.setEndTime(p.getExchangeTime());
        prize.setExchangeStatus(0);
        prize.setCreatedUser("管理员");
        prize.setModifiedUser("管理员");
        prize.setCreatedTime(localDateTime);
        prize.setModifiedTime(localDateTime);
        lotterypoolMapper.insertPrize(prize);
        System.out.println("-----------完成数据写入中奖表-----------");
//                        转盘表奖品数量扣减
        lotterypoolMapper.updatePoolOnePhone(p.getId());
        System.out.println("-----------完成奖品数量扣减-----------");
        }
        }
//           清空zset缓存
        while (iterator.hasNext()){
        String message=iterator.next();
        RedisMessageQueue.deleteMessageQueue(queueMessage,message);
        iterator.remove();
        }
        System.out.println("-----------抽奖结束清除队列缓存。-----------");

        }


    /* @param
    *   查看中奖纪录
    * */
    @Override
    public Prize showPrize(String phone,String topName) {
//        参数校验
        if (phone==null){
            throw new ParameterIsEmptyException("查看中奖率几率传来的参数为空！");
        }
//        从redis的Hash中获取该用户的中奖时间
        String nowTimeStr=RedisHashUtil.hget("lottery:prize",phone);
        //        参数校验
        if (nowTimeStr==null){
            throw new ParameterIsEmptyException("redis中获取的中奖时间为空！");
        }
        DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime localDateTime=LocalDateTime.parse(nowTimeStr,dateTimeFormatter);
//       根据手机号和中奖时间查出用户中奖信息
      Prize prize=lotterypoolMapper.selectPrizeTwoPhoneandPrizeDate(phone,localDateTime);
        if (prize==null){
            System.out.println("用户没中奖！");
            return null;
        }
//        直接返回中奖转盘id
        return prize;
    }

    @Override
    public Integer showCount(String phone, String name) {
//        参数校验
        if (phone==null||name==null){
            throw new ParameterIsEmptyException("参数为空！");
        }
//        查询抽奖池信息
         Lottery lottery=lotterypoolMapper.selectOneTopName(name);
        if (lottery==null){
            throw new SelectExcepion("查询错误！");
        }

        //        重建缓存
        if (!RedisUtils.isExist("lottery_"+phone+"_"+lottery.getId())){
            redisUtils.addRedis("lottery_"+phone+"_"+lottery.getId(),String.valueOf(0));
        }
//        从缓存中获取抽奖次数
        String count= RedisUtils.getValue("lottery_"+phone+"_"+lottery.getId());
        Integer result=Integer.parseInt(count);
        System.out.println("查询缓存抽奖次数成："+result);
        return result;
    }

    @Override
    public void reduceCount(String phone, String name) {
        //        参数校验
        if (phone==null||name==null){
            throw new ParameterIsEmptyException("参数为空！");
        }
//        查询抽奖池信息
        Lottery lottery=lotterypoolMapper.selectOneTopName(name);
        if (lottery==null){
            throw new SelectExcepion("查询错误！");
        }
//        从缓存中获取抽奖次数
        String count= RedisUtils.getValue("lottery_"+phone+"_"+lottery.getId());
        if (count==null){
            throw new SelectExcepion("查询缓存失败！");
        }
        Integer result=Integer.parseInt(count);
        result--;
        if (result<=0){
            result=0;
        }
//        存入缓存
        redisUtils.addRedis("lottery_"+phone+"_"+lottery.getId(),String.valueOf(result));
        System.out.println("消耗一次抽奖次数缓存抽奖次数剩余："+result);
    }

    @Override
    public List<ShowHotPrize> showHotPrize(String name, Integer currentPage, Integer pagePize) {
//        参数校验
        if (name==null||currentPage==null||pagePize==null){
            throw new ParameterIsEmptyException("参数为空！");
        }
//        查询抽奖池信息
          Lottery lottery=lotterypoolMapper.selectOneTopName(name);
        if (lottery==null){
            throw new SelectExcepion("抽奖池不存在！");
        }
//        查询转盘信息
        List<Turntable> turntables=lotterypoolMapper.selectOneTurntable(lottery.getId());
        if (turntables==null||turntables.size()<=0){
            throw new SelectExcepion("转盘信息不存在！");
        }
        Integer turnId1=-1;
        Integer turnId2=-1;
//        遍历转盘
        for (Turntable t:turntables) {
            if (t.getTurntableId() == 2) {
                turnId1 = t.getId();
            }
            if (t.getTurntableId() == 4) {
                turnId2 = t.getId();
            }
        }
//            查询中奖数据
            List<Prize> prizes=lotterypoolMapper.showHotPrize(turnId1,turnId2,currentPage,pagePize);
        System.out.println("能来到查询中奖数据："+prizes);
            if (prizes==null||prizes.size()<=0){
                throw new SelectExcepion("奖品数据不存在！");
            }
            List<ShowHotPrize> showHotPrizes=new ArrayList<>();
//            遍历查询用户信息
            for (Prize p:prizes){
//                查询讯用户信息
                User user=userMapper.selectPhone(p.getPhone());
                if (user==null){
//                    考虑到有些被封号所以查不到这个人,直接跳到下一轮循环
                    continue;
                }
                ShowHotPrize showHotPrize=new ShowHotPrize();
                showHotPrize.setAvatar(user.getAvatar());
                showHotPrize.setPhone(user.getPhone());
                showHotPrize.setPrizeTime(p.getPrizeTime());
                showHotPrize.setName(p.getName());
                showHotPrizes.add(showHotPrize);
            }
        System.out.println("完成中奖数据实时："+showHotPrizes);
        return showHotPrizes;
    }


}
